package org.web.accesscontrollist.server.biz.impl;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import org.web.accesscontrollist.sdk.biz.AclResourceTypeBiz;
import org.web.accesscontrollist.sdk.dto.*;
import org.web.accesscontrollist.sdk.dto.query.QueryAclResourceTypeRequest;
import org.web.accesscontrollist.sdk.dto.redundancy.TypeRedundancyDTO;
import org.web.accesscontrollist.sdk.helper.AclHelper;
import org.web.accesscontrollist.server.domain.AclResourceDetailDO;
import org.web.accesscontrollist.server.domain.AclResourceTypeDO;
import org.web.accesscontrollist.server.domain.AclResourceUserBindingDO;
import org.web.accesscontrollist.server.domain.query.QueryAclResourceDetail;
import org.web.accesscontrollist.server.domain.query.QueryAclResourceType;
import org.web.accesscontrollist.server.domain.query.QueryAclResourceUserBinding;
import org.web.accesscontrollist.server.service.AclResourceDetailService;
import org.web.accesscontrollist.server.service.AclResourceTypeService;
import org.web.accesscontrollist.server.service.AclResourceUserBindingService;
import org.web.base.domain.*;
import org.web.base.domain.exception.ResultMessageEnum;
import org.web.base.domain.exception.ServiceException;
import org.web.base.domain.helper.ResultHelper;
import org.web.base.domain.helper.ServiceExceptionHelper;
import org.web.base.domain.helper.ValidationHelper;

import jakarta.annotation.Resource;
import java.util.*;

@Service("aclResourceTypeBiz")
public class AclResourceTypeBizImpl implements AclResourceTypeBiz {

    private static final Logger logger = Logger.getLogger(AclResourceTypeBizImpl.class);

    private static final Set<String> querySimpleSet = new HashSet<String>() {{
        add("acl_resource_type_id");
        add("business_line");
        add("type_name");
        add("create_time");
        add("is_delete");
    }};


    @Resource
    private AclResourceTypeService aclResourceTypeService;

    @Resource
    private AclResourceDetailService aclResourceDetailService;

    @Resource
    private AclResourceUserBindingService aclResourceUserBindingService;

    @Override
    public ResultDO<List<AclResourceTypeDTO>> saveAclResourceTypeBatch(AclResourceTypeBatchDTO aclResourceTypeBatchDTO) {
        ResultDO<List<AclResourceTypeDTO>> resultDO = new ResultDO<>();
        OperatorDTO operatorDTO = null;
        try {
            // 参数校验
            AclHelper.checkSaveAclResourceTypeBatchDTO(aclResourceTypeBatchDTO, UpdateGroup.class);

            resultDO.setDomain(aclResourceTypeBatchDTO.getAclResourceTypeDTOList());
            // 判断业务线businessLine是否存在, 则校验权限。
            operatorDTO = aclResourceTypeBatchDTO.getOperatorDTO();
            // aclBusinessLineServiceBiz.judgeOperatorOrBuildDefaultBusinessLine(operatorDTO);

            List<String> typeNameList = new ArrayList<>();
            Map<String, AclResourceTypeDTO> mapBuildByAclResourceTypeDTOList = new HashMap<>();
            // 通过一次查询，查出数据库所有的数据。不在循环中访问数据库，减少数据库操作次数。
            for (AclResourceTypeDTO aclResourceTypeDTO : aclResourceTypeBatchDTO.getAclResourceTypeDTOList()) {
                AclHelper.addIfNotDuplicate(typeNameList, aclResourceTypeDTO.getTypeName());
                mapBuildByAclResourceTypeDTOList.put(aclResourceTypeDTO.getTypeName(), aclResourceTypeDTO);
            }

            QueryAclResourceType queryAclResourceType = new QueryAclResourceType();
            queryAclResourceType.setAllRecords();
            queryAclResourceType.setBusinessLine(aclResourceTypeBatchDTO.getOperatorDTO().getBusinessLine());
            queryAclResourceType.setTypeNameList(typeNameList);
            queryAclResourceType.setSpecifiedAttributeSet(querySimpleSet);
            List<AclResourceTypeDO> selectListFromDB = aclResourceTypeService.selectAclResourceTypeList(queryAclResourceType);

            // 遍历从数据库获取的数据。
            Map<String, AclResourceTypeDO> mapFromDB = new HashMap<>();
            for (AclResourceTypeDO aclResourceTypeDO : selectListFromDB) {
                mapFromDB.put(aclResourceTypeDO.getTypeName(), aclResourceTypeDO);
            }


            List<AclResourceTypeDO> insertList = new ArrayList<>();// 新增数据队列
            List<AclResourceTypeDO> updateList = new ArrayList<>();// 修改数据队列
            List<String> errorList = new ArrayList<>();
            int count = 0;
            for (AclResourceTypeDTO aclResourceTypeDTO : aclResourceTypeBatchDTO.getAclResourceTypeDTOList()) {
                ++count;
                AclResourceTypeDO aclResourceTypeDOFromDB = mapFromDB.get(aclResourceTypeDTO.getTypeName());
                if (aclResourceTypeDOFromDB == null) {
                    AclResourceTypeDO insertAclResourceTypeDO = new AclResourceTypeDO();
                    BeanUtils.copyProperties(insertAclResourceTypeDO, aclResourceTypeDTO);
                    // insert的场合，校验下数据合法性。
                    AclHelper.validate(count, errorList, AclResourceTypeDTO.class, aclResourceTypeDTO, InsertGroup.class);

                    insertList.add(insertAclResourceTypeDO);
                } else {
                    AclResourceTypeDO updateAclResourceTypeDO = new AclResourceTypeDO();
                    BeanUtils.copyProperties(updateAclResourceTypeDO, aclResourceTypeDTO);
                    // update的场合，不需要校验数据合法性。原因是前面的 AclHelper.checkSaveAclDetailBatchDTO(aclResourceTypeBatchDTO)已经校验。
                    updateAclResourceTypeDO.setAclResourceTypeId(aclResourceTypeDOFromDB.getAclResourceTypeId());
                    updateList.add(updateAclResourceTypeDO);
                }
            }

            if (!errorList.isEmpty()) {
                ServiceException serviceException = ServiceExceptionHelper.buildServiceException(ResultMessageEnum.PARAM_INVALID);
                serviceException.setErrorList(errorList);
                throw serviceException;
            }

            // 最后进行数据更新。
            for (AclResourceTypeDO aclResourceTypeDO : insertList) {
                aclResourceTypeService.insertAclResourceType(aclResourceTypeDO);
                supplementReturn(mapBuildByAclResourceTypeDTOList.get(aclResourceTypeDO.getTypeName()), aclResourceTypeDO, mapFromDB);
            }

            for (AclResourceTypeDO aclResourceTypeDO : updateList) {
                aclResourceTypeService.updateAclResourceTypeByAclResourceTypeId(aclResourceTypeDO);
                supplementReturn(mapBuildByAclResourceTypeDTOList.get(aclResourceTypeDO.getTypeName()), aclResourceTypeDO, mapFromDB);
            }

        } catch (Exception e) {
            logger.error(ServiceExceptionHelper.getExceptionInfo(e));
            resultDO = ResultHelper.buildResultDOByException(e, resultDO.getDomain());
        } finally {
            if (operatorDTO != null) {
                operatorDTO.setAccessKey(null);
            }
        }
        return resultDO;
    }

    @Override
    public ResultDO<Integer> executeAclResourceTypeRedundancyDTO(TypeRedundancyDTO typeRedundancyDTO) {
        ResultDO<Integer> resultDO = new ResultDO<>();

        try {
            AclHelper.validateTypeRedundancyDTO(typeRedundancyDTO);
            if ("Y".equals(typeRedundancyDTO.getIsDelete())) {
                // 删除的处理逻辑，如果detail表，存在type_name，则不允许删除，需要提前删除detail表中的type_name记录
                QueryAclResourceDetail queryAclResourceDetail = new QueryAclResourceDetail();
                queryAclResourceDetail.setBusinessLine(typeRedundancyDTO.getBusinessLine());
                queryAclResourceDetail.setTypeName(typeRedundancyDTO.getOldTypeName());
                Integer count = aclResourceDetailService.countAclResourceDetailList(queryAclResourceDetail);
                if (count > 0) {
                    throw ServiceExceptionHelper.buildServiceException(ResultMessageEnum.DATA_RECORD_EXIST, "有未删除的详情信息。请提前删除【资源详情】中typeName=" + typeRedundancyDTO.getOldTypeName() + "信息。遗留记录数： " + count);
                }

                AclResourceTypeDO aclResourceTypeDO = getAclResourceTypeDO(typeRedundancyDTO);
                AclResourceTypeDO deleteAclResourceTypeDO = new AclResourceTypeDO();
                deleteAclResourceTypeDO.setAclResourceTypeId(aclResourceTypeDO.getAclResourceTypeId());
                deleteAclResourceTypeDO.setIsDelete("Y");
                aclResourceTypeService.updateAclResourceTypeByAclResourceTypeId(deleteAclResourceTypeDO);
            } else {
                AclResourceTypeDO aclResourceTypeDO = getAclResourceTypeDO(typeRedundancyDTO);
                // 根据TypeName获取要修改的Binding信息。
                QueryAclResourceUserBinding queryAclResourceUserBinding = getQueryAclResourceUserBinding(typeRedundancyDTO);
                List<AclResourceUserBindingDO> selectAclResourceUserBindingList = aclResourceUserBindingService.selectAclResourceUserBindingList(queryAclResourceUserBinding);
                List<AclResourceUserBindingDO> updateBindingList = new ArrayList<>();
                for (AclResourceUserBindingDO aclResourceUserBindingDO : selectAclResourceUserBindingList) {
                    AclResourceUserBindingDO updateBindingDO = new AclResourceUserBindingDO();
                    updateBindingDO.setAclResourceUserBindingId(aclResourceUserBindingDO.getAclResourceUserBindingId());
                    updateBindingDO.setAclResourceTypeId(aclResourceTypeDO.getAclResourceTypeId());
                    updateBindingDO.setTypeName(typeRedundancyDTO.getNewTypeName());
                    updateBindingList.add(updateBindingDO);
                }

                // 根据TypeName获取要修改的Detail信息。用外键关联。
                QueryAclResourceDetail queryAclResourceDetail = getQueryAclResourceDetail(typeRedundancyDTO);
                List<AclResourceDetailDO> selectAclResourceDetaiList = aclResourceDetailService.selectAclResourceDetailList(queryAclResourceDetail);
                List<AclResourceDetailDO> updateDetailList = new ArrayList<>();
                for (AclResourceDetailDO aclResourceDetailDO : selectAclResourceDetaiList) {
                    AclResourceDetailDO updateDetailDO = new AclResourceDetailDO();
                    updateDetailDO.setAclResourceDetailId(aclResourceDetailDO.getAclResourceDetailId());
                    updateDetailDO.setTypeName(typeRedundancyDTO.getNewTypeName());
                    updateDetailDO.setAclResourceTypeId(aclResourceTypeDO.getAclResourceTypeId());
                    updateDetailList.add(updateDetailDO);
                }

                HashMap<String, Object> updateInfoMap = new HashMap<>();
                resultDO.setExtendsDomain(updateInfoMap);
                // 更新Binding
                for (AclResourceUserBindingDO updateBindingDO : updateBindingList) {
                    aclResourceUserBindingService.updateAclResourceUserBindingByAclResourceUserBindingId(updateBindingDO);
                }
                updateInfoMap.put("binding", updateBindingList.size());

                // 更新Detail
                for (AclResourceDetailDO updateDetailDO : updateDetailList) {
                    aclResourceDetailService.updateAclResourceDetailByAclResourceDetailId(updateDetailDO);
                }
                updateInfoMap.put("detail", updateDetailList.size());

                // 更新Type
                AclResourceTypeDO updateAclResourceTypeDO = new AclResourceTypeDO();
                updateAclResourceTypeDO.setAclResourceTypeId(aclResourceTypeDO.getAclResourceTypeId());
                updateAclResourceTypeDO.setTypeName(typeRedundancyDTO.getNewTypeName());
                aclResourceTypeService.updateAclResourceTypeByAclResourceTypeId(updateAclResourceTypeDO);
                updateInfoMap.put("type", 1);
                resultDO.setDomain(updateBindingList.size() + updateDetailList.size() + 1);
            }
        } catch (Exception e) {
            logger.error(ServiceExceptionHelper.getExceptionInfo(e));
            resultDO = ResultHelper.buildResultDOByException(e);
        }
        return resultDO;
    }

    private static QueryAclResourceDetail getQueryAclResourceDetail(TypeRedundancyDTO typeRedundancyDTO) {
        QueryAclResourceDetail queryAclResourceDetail = new QueryAclResourceDetail();
        // queryAclResourceDetail.setAclResourceTypeId(aclResourceTypeDO.getAclResourceTypeId());
        queryAclResourceDetail.setBusinessLine(typeRedundancyDTO.getBusinessLine());
        queryAclResourceDetail.setTypeName(typeRedundancyDTO.getOldTypeName());
        queryAclResourceDetail.setAllRecords();
        queryAclResourceDetail.setSpecifiedAttributeSet(new HashSet<String>() {{
            add("acl_resource_detail_id");
        }});
        return queryAclResourceDetail;
    }

    private static QueryAclResourceUserBinding getQueryAclResourceUserBinding(TypeRedundancyDTO typeRedundancyDTO) {
        QueryAclResourceUserBinding queryAclResourceUserBinding = new QueryAclResourceUserBinding();
        queryAclResourceUserBinding.setBusinessLine(typeRedundancyDTO.getBusinessLine());
        queryAclResourceUserBinding.setTypeName(typeRedundancyDTO.getOldTypeName());
        queryAclResourceUserBinding.setAllRecords();
        queryAclResourceUserBinding.setSpecifiedAttributeSet(new HashSet<String>() {{
            add("acl_resource_user_binding_id");
        }});
        return queryAclResourceUserBinding;
    }

    @Override
    public ResultDO<List<AclResourceTypeDTO>> selectAclResourceTypeList(QueryAclResourceTypeRequest queryAclResourceTypeRequest) {
        ResultDO<List<AclResourceTypeDTO>> resultDO = new ResultDO<>();
        try {
            ValidationHelper.validateWithThrow(QueryAclResourceTypeRequest.class, queryAclResourceTypeRequest);
            QueryAclResourceType queryAclResourceType = new QueryAclResourceType();
            BeanUtils.copyProperties(queryAclResourceType, queryAclResourceTypeRequest);
            ResultDO<List<AclResourceTypeDO>> selectResult = aclResourceTypeService.selectAclResourceTypeListResult(queryAclResourceType);
            if (selectResult.isSuccess()) {
                List<AclResourceTypeDTO> list = new ArrayList<>();
                resultDO.setDomain(list);
                for (AclResourceTypeDO aclResourceTypeDO : selectResult.getDomain()) {
                    AclResourceTypeDTO aclResourceTypeDTO = new AclResourceTypeDTO();
                    BeanUtils.copyProperties(aclResourceTypeDTO, aclResourceTypeDO);
                    list.add(aclResourceTypeDTO);
                }
                resultDO.setTotalCount(selectResult.getTotalCount());
            } else {
                BeanUtils.copyProperties(resultDO, selectResult);
            }
        } catch (Exception e) {
            logger.error(ServiceExceptionHelper.getExceptionInfo(e));
            resultDO = ResultHelper.buildResultDOByException(e);
        }
        return resultDO;
    }

    private AclResourceTypeDO getAclResourceTypeDO(TypeRedundancyDTO aclResourceTypeRedundancyDTO) {
        QueryAclResourceType queryAclResourceType = new QueryAclResourceType();
        queryAclResourceType.setFirstRecord();
        queryAclResourceType.setBusinessLine(aclResourceTypeRedundancyDTO.getBusinessLine());
        queryAclResourceType.setTypeName(aclResourceTypeRedundancyDTO.getOldTypeName());
        AclResourceTypeDO aclResourceTypeDO = aclResourceTypeService.selectOneAclResourceType(queryAclResourceType);
        if (aclResourceTypeDO == null) {
            throw ServiceExceptionHelper.buildServiceException(ResultMessageEnum.DATA_RECORD_NOT_EXIST, "修改的记录不存在。" + aclResourceTypeRedundancyDTO);
        }
        return aclResourceTypeDO;
    }

    private void supplementReturn(AclResourceTypeDTO aclResourceTypeDTO, AclResourceTypeDO aclResourceTypeDO, Map<String, AclResourceTypeDO> mapFromDB) {
        aclResourceTypeDTO.setAclResourceTypeId(aclResourceTypeDO.getAclResourceTypeId());
        AclResourceTypeDO fromDB = mapFromDB.get(aclResourceTypeDO.getTypeName());
        if (fromDB != null) {
            aclResourceTypeDTO.setCreateTime(fromDB.getCreateTime());
            aclResourceTypeDTO.setIsDelete(fromDB.getIsDelete());
        } else {
            aclResourceTypeDTO.setCreateTime(aclResourceTypeDO.getCreateTime());
            aclResourceTypeDTO.setIsDelete(aclResourceTypeDO.getIsDelete());
        }
        aclResourceTypeDTO.setUpdateTime(aclResourceTypeDO.getUpdateTime());
        aclResourceTypeDTO.setExecuteSuccess(true);
    }

}
